﻿using CommandMessenger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace Fawcett.Plugins.Arduino
{
    public enum Command
    {
        kCOMM_ERROR = 000, // Lets Arduino report serial port comm error back to the PC (only works for some comm errors)
        kACK = 001, // Arduino acknowledges cmd was received
        kARDUINO_READY = 002, // After opening the comm port, send this cmd 02 from PC to check arduino is ready
        kERR = 003, // Arduino reports badly formatted cmd, or cmd not recognised
        kHeadingChange = 004,
        kSwitchChange = 005,
        OnSetSingleBitOutput = 006,    //101   
        OnSetAnalogueOutput = 007,    //102             // Command to report errors
        NullCommand = 008,
        kVersion = 009,
        kMessage = 010,
        kIdentity = 011,
    };


    public class ArduinoBoard
    {
        private  CmdMessenger cmdMessenger;
        public int Ident { get; set;  } 
        public string Version { get; set; }
        /// <summary>
        /// Constructor 
        /// </summary>
        /// <param name="cmdMessenger1"></param>
        public ArduinoBoard(CmdMessenger cmdMessenger1)
        {
            this.cmdMessenger = cmdMessenger1;

            AttachCommandCallBacks();

            cmdMessenger.StartListening();
        }

        /// <summary>
        /// Spoof some test data
        /// </summary>
        /// <param name="ident"></param>
        /// <param name="version"></param>
        public ArduinoBoard(int ident, string version)
        {
            // TODO: Complete member initialization
            this.Ident = ident;
            this.Version = version;
        }

        public void SetValue(Command cmd, int channel, int value)
        {
            if (cmd == Command.kSwitchChange) return;

            GlobalEvent.FireEvent( this , new EPlugin( PluginCode.MESSAGE, string.Format("Sending to Arduino {1} (PIN= {2}) {3}", cmd.ToString() , channel , value)));

            var commandPlainText = new SendCommand((int)cmd);

            if (cmd == Command.OnSetSingleBitOutput)
            {
                commandPlainText.AddArgument(channel);
                commandPlainText.AddArgument(value);
            }

            if (cmd == Command.OnSetAnalogueOutput)
            {
                commandPlainText.AddArgument(value);
            }

            if (cmdMessenger != null) cmdMessenger.SendCommand(commandPlainText);
        }

        private void AttachCommandCallBacks()
        {

            if (cmdMessenger == null) return;

            GlobalEvent.FireEvent(this, new EPlugin(PluginCode.MESSAGE, string.Format("Attaching Call backs to Arduino")));

            cmdMessenger.Attach(OnUnknownCommand);
            cmdMessenger.Attach((int)Command.kACK,          Acknowledge);
            cmdMessenger.Attach((int)Command.kSwitchChange, OnRecieveSingleBitInput);
            cmdMessenger.Attach((int)Command.kERR,          ErrorRecieved);
            cmdMessenger.Attach((int)Command.kMessage,      this.ArduinoMessage);

        }

        private void OnRecieveSingleBitInput(ReceivedCommand arguments)
        {
            int p1 = arguments.ReadInt32Arg();
            int p2 = arguments.ReadInt32Arg();


            GlobalEvent.FireEvent(this, new EPlugin(PluginCode.FROM_ARDUINO,  p2.ToString(), p1));

            GlobalEvent.FireEvent(this, new EPlugin(PluginCode.MESSAGE, string.Format("Arduino Switch {1} changed {2}" , p2 , p1 )));

        }


        private void ErrorRecieved(ReceivedCommand arguments)
        {
            GlobalEvent.FireEvent(this, new EPlugin(PluginCode.ERROR, string.Format("Arduino Error {1} ", arguments.ReadStringArg())));
        }

        private void ArduinoMessage(ReceivedCommand arguments)
        {
            Console.WriteLine(arguments.ReadStringArg());
        }

        private void Acknowledge(ReceivedCommand arguments)
        {
            var p1 = arguments.ReadInt32Arg();
            var p2 = arguments.ReadInt32Arg();

            GlobalEvent.FireEvent(this, new EPlugin(PluginCode.MESSAGE, string.Format("Arduino Command Acknowleged P1={1} P2{2}", p2, p1)));

        }

        private void OnUnknownCommand(ReceivedCommand arguments)
        {
            GlobalEvent.FireEvent(this, new EPlugin(PluginCode.ERROR, string.Format("Arduino Command without attached callback received {1} ", arguments.CommandId)));
        }

        public void Close()
        {

        }

        public string Register()
        {
            var commandPlainText = new SendCommand((int)Command.kVersion);
            if (cmdMessenger != null) cmdMessenger.SendCommand(commandPlainText);

            return ("CONSOLE");
        }
    }
}
